package gnss_plugs

import (
	"bytes"
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

/***
 *  不推荐使用
 */
type GnssRinexEph3xWriter struct {
	Id             string
	bodyCnt        int
	curTmpFileName string
	lastFileName   string
	tempDir        string

	// 如果不为255, 则进行过滤
	Sys       byte
	Filefmt   string
	tmpPath   string
	startTime time.Time
	currTime  time.Time
	ionosMap  map[string]*Rinex2XIonosRec
	recMap    map[string]time.Time

	filter_start time.Time
	filter_end   time.Time

	// 检测系统重入bug
	lastgid          uint64
	checkworkingflag int32
}

func NewGnssRinexEph3xWriter() *GnssRinexEph3xWriter {
	rval := &GnssRinexEph3xWriter{
		Id:       utils.RandKeyString(4),
		Filefmt:  "",
		Sys:      255,
		tempDir:  TempFilesDirectory,
		currTime: time.Time{},
		ionosMap: make(map[string]*Rinex2XIonosRec),
		recMap:   make(map[string]time.Time),
	}
	return rval
}

func (this *GnssRinexEph3xWriter) checkNewFile(curT time.Time) bool {
	str := this.Filefmt
	if curT.IsZero() {
		curT = time.Now().UTC()
	}
	str = utils.ParseTimeFmt(str, curT)
	return strings.Compare(this.lastFileName, str) != 0
}

func (this *GnssRinexEph3xWriter) checkFileStart() {
	if len(this.curTmpFileName) == 0 {
		if this.startTime.IsZero() {
			this.startTime = time.Now().UTC()
		}
		strFileN := utils.ParseTimeFmt(this.Filefmt, this.startTime)
		this.lastFileName = strFileN
		utils.ExtractFileNameWithOutPathAndExt(strFileN)
		this.curTmpFileName = this.tempDir + utils.PathSeparator + "~eph." + utils.ExtractFileNameWithOutPathAndExt(strFileN) + ".tmp"
		utils.ForceCreateFilePath(this.curTmpFileName)
		utils.ForceCreateFilePath(this.lastFileName)
	}
}

func (this *GnssRinexEph3xWriter) ConfigFilterTimeStr(start, end string) {
	this.filter_start = utils.TryStrToTime(start, time.UTC, utils.ZeroTime)
	this.filter_end = utils.TryStrToTime(end, time.UTC, utils.ZeroTime)

}

func (this *GnssRinexEph3xWriter) ConfigFilterTime(start, end time.Time) {
	this.filter_start = start
	this.filter_end = end
}

/*
**
 */
func (this *GnssRinexEph3xWriter) ConfigTempDir(val string) {
	val = strings.TrimLeftFunc(val, func(r rune) bool {
		return r == '/' || r == '\\' // 去掉最后一个路径分隔符
	})
	this.tempDir = val
}

func (this *GnssRinexEph3xWriter) CurrentTempFileName() string {
	return this.curTmpFileName
}

func (this *GnssRinexEph3xWriter) Stop() {
	this.checkFileEnd()
}

/**
 * 将临时文件写入到一个文件
 */
func (this *GnssRinexEph3xWriter) WriteCache2File(filename string) int {
	if len(this.curTmpFileName) == 0 {
		return 0
	}

	// first line
	strHeader := utils.AddSuffixForWidth("     3.03           N: GNSS NAV DATA    M: Mixed", 60, " ")
	strHeader += "RINEX VERSION / TYPE\n"
	strHeader += "     4     4   574     1 BDT                                LEAP SECONDS\n"
	strHeader += "    18    18  1930     1 GPS                                LEAP SECONDS\n"
	strHeader += "    18    18  1930     1 GAL                                LEAP SECONDS\n"
	strHeader += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		utils.AddSuffixForWidth(this.startTime.Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	if this.startTime.IsZero() {
		this.startTime = time.Now().UTC()
	}

	strHeader += this.getIonosHeader()
	strHeader += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"

	utils.NewFileData(filename, []byte(strHeader))
	utils.AppendAllFile(filename, this.curTmpFileName)
	return this.bodyCnt
}

func (this *GnssRinexEph3xWriter) checkFileEnd() {
	if len(this.curTmpFileName) == 0 {
		return
	}
	this.WriteCache2File(this.lastFileName)
	os.Remove(this.curTmpFileName)
	this.curTmpFileName = ""
	this.ionosMap = make(map[string]*Rinex2XIonosRec)
	this.recMap = make(map[string]time.Time)
	this.startTime = utils.ZeroTime
	this.bodyCnt = 0
}

func (this *GnssRinexEph3xWriter) checkGetIonosRec(hcStr string) *Rinex2XIonosRec {
	itm := this.ionosMap[hcStr]
	if itm == nil {
		itm = &Rinex2XIonosRec{}
		itm.timeid = hcStr
		this.ionosMap[hcStr] = itm
	}
	return itm
}

func (this *GnssRinexEph3xWriter) getIonosHeader() string {
	headers := make([]*Rinex2XIonosRec, 0, len(this.ionosMap))
	for _, v := range this.ionosMap {
		headers = append(headers, v)
	}

	sort.Slice(headers, func(i, j int) bool {
		return strings.Compare(headers[i].timeid, headers[j].timeid) < 0
	})

	str := ""
	for _, rec := range headers {
		str += rec.igmas31 + rec.igmas32 + rec.igmas33 + rec.igmas34
	}

	return str
}

/**
 * 不多线程重入
 */
func (this *GnssRinexEph3xWriter) OnRecvGnssRecCallBack(sender interface{}, channelid string, subscribeid interface{}, data interface{}, tag interface{}, isBreak *bool) bool {
	if atomic.CompareAndSwapInt32(&this.checkworkingflag, 0, 1) {
		this.lastgid = utils.GetCurrentGoRoutineID()
		defer func() {
			this.checkworkingflag = 0
		}()
		if data == nil {
			this.checkFileEnd()
			return true
		}
		rtcmRec, ok := data.(*gnss.GnssRtcmData)
		if ok {
			this.OnRecvGnssDataRec(sender, rtcmRec)
		}
		return true
	} else {
		lastid := this.lastgid
		str := fmt.Sprintf("[并发重入]%s, lastgid:%d, callstack:%s", this.Id, this.lastgid, utils.GetGoroutineStack(lastid))
		utils.Log.WarnTypef("BUG", str)
		panic(str)
		return false
	}
}

func (this *GnssRinexEph3xWriter) OnRecvGnssDataRec(sender interface{}, rec *gnss.GnssRtcmData) {
	if this.Sys != 255 && rec.Sys != this.Sys {
		return
	}

	if rec.TypeId == gnss.TYPE_EPH {
		if eph, ok := rec.Data.(*gnss.RtcmEph); ok {
			err1 := eph.CheckSatIdInvalid()
			if err1 != nil {
				utils.Log.Warnf("[RTCM数据错误]:%s\r\n", err1.Error(), utils.BufToHexStr(rec.RawBuf, -1, " "))
			}

			if !utils.TimeIsUnixZero(this.filter_start) {
				if rec.RtcmTime.Before(this.filter_start) {
					return
				}
			}

			if !utils.TimeIsUnixZero(this.filter_end) {
				if rec.RtcmTime.After(this.filter_end) {
					return
				}
			}

			if this.checkNewFile(rec.RtcmTime) {
				this.checkFileEnd() // 上个文件处理

				this.startTime = rec.RtcmTime

				if this.startTime.IsZero() {
					this.startTime = time.Now().UTC()
				}

				// 初始化新文件
				this.checkFileStart()
			}

			// 去复重复
			checkKey := fmt.Sprintf("%s_%s_%d", eph.GetPrnStrCode(), utils.DateTimeString(eph.Time), eph.MsgId)
			t1 := this.recMap[checkKey]

			if !t1.IsZero() && time.Now().Sub(t1).Seconds() < 300 { // 5分钟内只存放一条
				return
			}

			this.recMap[checkKey] = time.Now()

			str := eph.GetRinexStr()
			_, err := utils.AppendFile(this.curTmpFileName, []byte(str))
			if err != nil {
				utils.Log.Errf("写入出现异常:%s, 文件:%s", err.Error(), this.curTmpFileName)
			}
			this.bodyCnt++
		}

		return
	}

	hcStr := utils.ToHourChr(time.Now().UTC())
	if !rec.RefUtc.IsZero() {
		hcStr = utils.ToHourChr(rec.RefUtc)
	}

	if rec.MsgId == 31 {
		if rec31, ok := rec.Data.(*gnss.Gnss31Bds); ok {
			itm := this.checkGetIonosRec(hcStr)
			str := fmt.Sprintf("BDSA %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec31.BDS_Alpha0, 4), FPrintRinexEV(rec31.BDS_Alpha1, 4),
				FPrintRinexEV(rec31.BDS_Alpha2, 4), FPrintRinexEV(rec31.BDS_Alpha3, 4), hcStr)
			str += fmt.Sprintf("BDSB %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec31.BDS_Beta0, 4), FPrintRinexEV(rec31.BDS_Beta1, 4),
				FPrintRinexEV(rec31.BDS_Beta2, 4), FPrintRinexEV(rec31.BDS_Beta3, 4), hcStr)
			itm.igmas31 = str

			//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
		}
	} else if rec.MsgId == 32 {
		if bds32Rec, ok := rec.Data.(*gnss.Gnss32Bds); ok {
			rec32 := bds32Rec

			str := fmt.Sprintf("BDS1 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha1, 4), FPrintRinexEV(rec32.BDS_Alpha2, 4),
				FPrintRinexEV(rec32.BDS_Alpha3, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)
			str += fmt.Sprintf("BDS2 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha4, 4), FPrintRinexEV(rec32.BDS_Alpha5, 4),
				FPrintRinexEV(rec32.BDS_Alpha6, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)
			str += fmt.Sprintf("BDS3 %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec32.BDS_Alpha7, 4), FPrintRinexEV(rec32.BDS_Alpha8, 4),
				FPrintRinexEV(rec32.BDS_Alpha9, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)

			itm := this.checkGetIonosRec(hcStr)
			itm.igmas32 = str
			//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
		}
	} else if rec.MsgId == 33 {
		if rec33, ok := rec.Data.(*gnss.IgmasIonos); ok {

			str := fmt.Sprintf("GPSA %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec33.Alpha0, 4), FPrintRinexEV(rec33.Alpha1, 4),
				FPrintRinexEV(rec33.Alpha2, 4), FPrintRinexEV(rec33.Alpha3, 4), hcStr)
			str += fmt.Sprintf("GPSB %s%s%s%s %s     IONOSPHERIC CORR\n", FPrintRinexEV(rec33.Beta0, 4), FPrintRinexEV(rec33.Beta1, 4),
				FPrintRinexEV(rec33.Beta2, 4), FPrintRinexEV(rec33.Beta3, 4), hcStr)

			itm := this.checkGetIonosRec(hcStr)
			itm.igmas33 = str
			//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
		}
	} else if rec.MsgId == 34 {
		if rec34, ok := rec.Data.(*gnss.IgmasGalileoIonos); ok {
			str := fmt.Sprintf("GAL  %s%s%s%s %s     IONOSPHERIC CORR\n",
				FPrintRinexEV(rec34.Ai0, 4), FPrintRinexEV(rec34.Ai1, 4),
				FPrintRinexEV(rec34.Ai2, 4), string(bytes.Repeat([]byte(" "), 12)), hcStr)

			itm := this.checkGetIonosRec(hcStr)
			itm.igmas34 = str
			//utils.Log.Infof("[%d] rinex str:%s\r\nrawbuf:%s", rec.MsgId, str, utils.BufToHexStr(rec.RawBuf, len(rec.RawBuf), " "))
		}
	}
}
